﻿using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using Xppend.CodeGenerator.Decorators;

namespace Xppend.CodeGenerator
{
    // https://learn.microsoft.com/zh-cn/dotnet/csharp/roslyn-sdk/source-generators-overview
    [Generator]
    public class XppendSourceGenerator : ISourceGenerator
    {

        public void Initialize(GeneratorInitializationContext context)
        {
            //if (!Debugger.IsAttached)
            //{
            //    Debugger.Launch();
            //}
        }

        public void Execute(GeneratorExecutionContext context)
        {
            // 找到 X 后缀的内容
            // 将 X 后缀的方法改写成 不带后缀的

            var syntaxTrees = context.Compilation.SyntaxTrees;

            foreach (var syntaxTree in syntaxTrees)
            {
                syntaxTree.TryGetRoot(out var root);
                if (root == null) continue;

                var filePath = syntaxTree.FilePath;
                if (IsIgnoreExt(filePath)) continue;

                //Console.WriteLine("syntaxTree: " + filePath); // [print it]
                AnalyzeRoot(root, context);
                // use define 
            }


        }

        ///<summary>忽略指定的文件。</summary>
        private bool IsIgnoreExt(string? path)
        {
            if (string.IsNullOrWhiteSpace(path)) return true;
            var exts = new string[] { ".g.cs" };
            foreach (var ext in exts)
            {
                if (path.EndsWith(ext, StringComparison.OrdinalIgnoreCase)) return true;
            }
            return false;
        }

        public void AnalyzeRoot(SyntaxNode? treeRoot, GeneratorExecutionContext context)
        {
            var classDeclares = treeRoot.DescendantNodes().OfType<ClassDeclarationSyntax>();
            var classWithParts = from cls in classDeclares where IsPatialClass(cls) select cls;

            foreach (var cls in classWithParts)
            {
                AnalyzeClass(cls, out var gensource);
                if (string.IsNullOrWhiteSpace(gensource) == false)
                {
                    var className = cls.Identifier.Text;
                    context.AddSource($"{className}.g.cs", gensource);
                }
            }
        }

        private bool IsPatialClass(ClassDeclarationSyntax classDeclaration)
        {
            var modifiers = classDeclaration.Modifiers;
            return modifiers.Any(t => t.Kind() == SyntaxKind.PartialKeyword);
        }

        ///<summary>分析类型，并且找到以X为结尾的方法。</summary>
        private void AnalyzeClass(ClassDeclarationSyntax classDeclaration, out string gensource)
        {
            var sb = new StringBuilder();
            BeginWritePartialClass(sb, classDeclaration); // {

            AnalyzeMethods(sb, classDeclaration);
            AnalyzeProperties(sb, classDeclaration);

            EndWriteParticalClass(sb, classDeclaration); // }

            gensource = sb.ToString();
        }

        ///<summary>分析方法。</summary>
        private void AnalyzeMethods(StringBuilder stringBuilder, ClassDeclarationSyntax classDeclaration)
        {
            var methodDeclares = classDeclaration.Members.OfType<MethodDeclarationSyntax>().Where(m => m.Identifier.Text.EndsWith("X")).ToArray();

            if (methodDeclares.Length == 0) return;

            foreach (var methodDeclare in methodDeclares)
            {
                WriteFakeMethod(stringBuilder, methodDeclare);
            }

        }

        ///<summary>分析属性。</summary>
        private void AnalyzeProperties(StringBuilder stringBuilder, ClassDeclarationSyntax classDeclaration)
        {
            var propertyDeclares = classDeclaration.Members.OfType<PropertyDeclarationSyntax>().ToArray();

            if (propertyDeclares.Length == 0) return;

            WriteFakeProperties(stringBuilder, classDeclaration.Identifier.Text, propertyDeclares);
        }

        private void BeginWritePartialClass(StringBuilder stringBuilder, ClassDeclarationSyntax classDeclaration)
        {
            var tree = classDeclaration.SyntaxTree;
            var root = tree.GetRoot();
            var usingDirectiveNodes = root.DescendantNodes().OfType<UsingDirectiveSyntax>();
            foreach (var usingDerectiveNode in usingDirectiveNodes)
            {
                stringBuilder.AppendLine(usingDerectiveNode.ToString());
            }

            var declareNamespace = root.DescendantNodes().OfType<NamespaceDeclarationSyntax>().FirstOrDefault();

            if (declareNamespace == null)
            {

            }
            else
            {
                var namespaceName = declareNamespace.Name;
                //Console.WriteLine($"{namespaceName}");
                stringBuilder.AppendLine($"namespace {namespaceName} ");
                stringBuilder.AppendLine("{");
            }

            var className = classDeclaration.Identifier.Value;
            var modifiers = classDeclaration.Modifiers.ToString();
            stringBuilder.AppendLine($"{modifiers} class {className}");
            stringBuilder.AppendLine("{");

        }

        private void EndWriteParticalClass(StringBuilder stringBuilder, ClassDeclarationSyntax classDeclaration)
        {
            stringBuilder.AppendLine("}"); // end class
            stringBuilder.AppendLine("}"); // end namespace
        }

        private void WriteFakeMethod(StringBuilder stringBuilder, MethodDeclarationSyntax methodDeclare)
        {
            var methodName = methodDeclare.Identifier.Text;
            if (methodName.Length <= 1) return;
            var fakeMethodName = methodName.Substring(0, methodName.Length - 1);
            var modifiers = methodDeclare.Modifiers.ToString();
            var paraList = methodDeclare.ParameterList.ToString();
            var returnType = methodDeclare.ReturnType.ToString();

            stringBuilder.AppendLine($"{modifiers} {returnType} {fakeMethodName}{paraList}");

            stringBuilder.AppendLine("{");

            //stringBuilder.AppendLine(methodDeclare.Body.ToString());

            //stringBuilder.AppendLine("Console.WriteLine(\"你正在使用织入方法哦 O(∩_∩)O\");");

            if (AppendCostTimeDecorator.Instance.HasDecorator(methodDeclare))
            {
                AppendCostTimeDecorator.Instance.Decorate(stringBuilder, methodDeclare);
            }

            stringBuilder.AppendLine("}");

        }

        private void WriteFakeProperty(StringBuilder stringBuilder, PropertyDeclarationSyntax propertyDeclare)
        {

        }

        private void WriteFakeProperties(StringBuilder stringBuilder, string className, IEnumerable<PropertyDeclarationSyntax> propertyDeclares)
        {
            var dataContext = new InjectConstructorDataContext() { ClassName = className, PropertyDeclarationList = propertyDeclares };
            if (InjectConstructorDecorator.Instance.HasDecorator(dataContext))
            {
                InjectConstructorDecorator.Instance.Decorate(stringBuilder, dataContext);
            }
        }
    }
}
